13 隐藏类:如何在内存中快速查找对象属性
为什么静态语言的效率更高
JavaScript 在运行时,对象的属性是可以被修改的,使用 start.x 时并不知道该对象中是否有 x,也不知道 x 相对于对象的偏移量是多少,V8 并不知道该对象的具体的形状。要查询对象 start 中的 x 属性时,V8 会按照具体的规则一步一步来查询,这个过程非常的慢且耗时。
C++ 在声明一个对象之前需要定义该对象的结构,代码在执行之前需要先被编译,编译的时候,每个对象的形状都是固定的,在代码的执行过程中,Point 的形状是 无法被改变的。访问一个对象的属性时,知道该属性相对于该对象地址的偏移值,没有任何中间的查找环节。
静态语言中,可以直接通过偏移量查询来查询对象的属性值,是静态语言的执行效率高的一个原因。
什么是隐藏类 (Hidden Class)
将 JavaScript 中的对象静态化,V8 在运行 JavaScript 的过程中,会假设 JavaScript 中的对象是静态的。
V8 为每个对象创建一个隐藏类,记录该对象一些基础的布局信息,包括对象中所包含的所有的属性和每个属性相对于对象的偏移量。
let point = { x: 100, y: 200 };
V8 执行到这段代码时,会先为 point 对象创建一个隐藏类,在 V8 中把隐藏类又称为 map,每个对象都有一个 map 属性,其值指向内存中的隐藏类。
多个对象共用一个隐藏类
如果两个对象的形状是相同的,V8 就会为其复用同一个隐藏类:
- 减少隐藏类的创建次数,也间接加速了代码的执行速度;
- 减少隐藏类的存储空间。
let point = { x: 100, y: 200 };
let point2 = { x: 3, y: 4 };
重新构建隐藏类
JavaScript 是动态语言,在执行过程中对象的形状是可以被改变的,如果某个对象的形状改变了,隐藏类也会随着改变,V8 要为新改变的对象重新构建新的隐藏类。
let point = {};
point.x = 100;
point.y = 200;
每次给对象添加了一个新属性之后,该对象的隐藏类的地址都会改变,也就意味着隐藏类也随着改变:
最佳实践
- 如果对象的形状没有发生改变,该对象就会一直使用该隐藏类;
- 如果对象的形状发生了改变,V8 会重建一个新的隐藏类给该对象。
开发建议:
- 使用字面量初始化对象时,要保证属性的顺序是一致的。
- 尽量使用字面量一次性初始化完整对象属性。
- 尽量避免使用 delete 方法。